package guzb.cnblogs.security.industrydemo.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 自定义的访问控制（权限）检查服务
 */
@Service
public class AuthorityService {

    @Autowired
    SecurityMapper securityMapper;

    public boolean currentUserCanAccess(String uri, String httpMethod) {
        // return true;

        SecurityContext securityContext = SecurityContextHolder.getContext();
        Authentication authentication = securityContext.getAuthentication();

        // 整个系统中，有访问控制要求的URI资源集合，即：这是一个所有需要做权限校验的web资源的集合
        List<AuthorityResourceItem> allAuthorityResources = securityMapper.listAllAuthorityResources();

        // 过滤出可访问指定 uri 的权限列表
        List<String> authoritiesOfUri = filterUriAuthorities(allAuthorityResources, uri, httpMethod);

        // 如果可访问指定uri的权限集合为空，表明这个uri是可以匿名访问的
        if(CollectionUtils.isEmpty(authoritiesOfUri)) {
            return true;
        }

        if (isAnonymous(authentication)) {
            return false;
        }

        List<String> userAuthorities = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
        return CollectionUtils.containsAny(authoritiesOfUri, userAuthorities);
    }

    private boolean isAnonymous(Authentication authentication) {
        return AnonymousAuthenticationToken.class.isAssignableFrom(authentication.getClass());
    }

    /**
     * 过滤出可访问指定URI的权限清单
     * @param allAuthorityResources 系统所有的 uri 资源
     * @param uri 要过滤的 uri
     * @param httpMethod 要过滤的 uri 的 http method
     */
    private List<String> filterUriAuthorities(List<AuthorityResourceItem> allAuthorityResources, String uri, String httpMethod) {
        return allAuthorityResources.stream().filter(resource -> {
            return uri.equalsIgnoreCase(resource.getResourceUri()) && httpMethod.equalsIgnoreCase(resource.getResourceHttpMethod());
        }).map(AuthorityResourceItem::getAuthority).collect(Collectors.toList());
    }

}
